﻿using Core.IRepository;
using Core.Models;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;


namespace Core.Repostory
{
    public class BaseRespostory : IBaseRepository
    {
        private readonly CoreContext context;

        public BaseRespostory(CoreContext context)
        {
            this.context = context;
        }

        public void Add<T>(T entity) where T : BaseModel
        {
            context.Set<T>().Add(entity);
        }

        public void Delete<T>(T entity) where T : BaseModel
        {
            context.Entry<T>(entity).State = Microsoft.EntityFrameworkCore.EntityState.Deleted;
        }

        public void Delete<T>(Expression<Func<T, bool>> expression) where T : BaseModel
        {
            var objs = context.Set<T>().Where(expression);
            foreach(var o in objs)
            {
                context.Set<T>().Remove(o);
            }
        }

        public void Delete<T>(int key) where T : BaseModel,new()
        {
            T obj = new T() { Id = key };
            context.Entry<T>(obj).State = EntityState.Deleted;
        }

        public DataTable ExecuteDataTable(string sql, params IDataParameter[] ps)
        {
            using (var conn = context.Database.GetDbConnection())
            {
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql;
                if (ps != null)
                    cmd.Parameters.AddRange(ps);
                var table = new DataTable();
                var reader = cmd.ExecuteReader();
                table.Load(reader);
                reader.Close();
                return table;
            }
        }

        public object ExecuteScalar(string sql, params IDataParameter[] ps)
        {
            using (var conn = context.Database.GetDbConnection())
            {
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql;
                if (ps != null)
                    cmd.Parameters.AddRange(ps);
                conn.Open();
                return cmd.ExecuteScalar();
            }
        }

        public int ExecuteNonQuery(string sql, params IDataParameter[] ps)
        {
            using (var conn = context.Database.GetDbConnection())
            {
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql;
                if (ps != null)
                    cmd.Parameters.AddRange(ps);
                conn.Open();
                return cmd.ExecuteNonQuery();
            }
          
        }

        public bool Exists<T>(Expression<Func<T, bool>> expression) where T : BaseModel
        {
            return context.Set<T>().Any(expression);
        }

        public T Find<T>(int key) where T : BaseModel
        {
            return context.Set<T>().FirstOrDefault(n => n.Id == key);
        }

        public async Task<T> FindAsync<T>(int key) where T : BaseModel
        {
            return await context.Set<T>().FirstOrDefaultAsync(n => n.Id == key);
        }

        public IQueryable<T> Find<T>() where T : BaseModel
        {
            return context.Set<T>();
        }

        public IQueryable<T> Find<T>(Expression<Func<T, bool>> expression) where T : BaseModel
        {
            return context.Set<T>().Where(expression);
        }

        public IQueryable<T> Find<T, TOrder>(Expression<Func<T, bool>> expression, Expression<Func<T, TOrder>> order, bool isAsc = true) where T : BaseModel
        {
            if (isAsc)
                return context.Set<T>().Where(expression).OrderBy(order);
            else
                return context.Set<T>().Where(expression).OrderByDescending(order);
        }

        public IQueryable<T> Find<T, TOrder>(int pageIndex, int pageSize, out int count, Expression<Func<T, bool>> expression = null, Expression<Func<T, TOrder>> order = null, bool isAsc = true) where T : BaseModel
        {
            IQueryable<T> query = context.Set<T>();
            if (expression == null)
            {
                count = context.Set<T>().Count();
            }
            else
            {
                count = context.Set<T>().Where(expression).Count();
                query = context.Set<T>().Where(expression);
            }
                      

            if (isAsc)
            {
                if (order == null)
                    query = query.OrderBy(o => o.Id);
                else
                    query = query.OrderBy(order);

            }
            else
            {
                if (order == null)
                    query = query.OrderByDescending(o => o.Id);
                else
                    query = query.OrderByDescending(order);
            }

            return query;
        }

        public void Update<T>(T entity) where T : BaseModel
        {
            context.Entry<T>(entity).State = EntityState.Modified;
        }

        public IQueryable<T> Find<T, TOrder>(int pageIndex, int pageSize, Expression<Func<T, bool>> expression, Expression<Func<T, TOrder>> order, out int count, bool isAsc = true) where T : BaseModel
        {
            IQueryable<T> query = context.Set<T>();
            if (expression == null)
            {
                count = context.Set<T>().Count();
            }
            else
            {
                count = context.Set<T>().Where(expression).Count();
                query = context.Set<T>().Where(expression);
            }


            if (isAsc)
            {
                if (order == null)
                    query = query.OrderBy(o => o.Id);
                else
                    query = query.OrderBy(order);

            }
            else
            {
                if (order == null)
                    query = query.OrderByDescending(o => o.Id);
                else
                    query = query.OrderByDescending(order);
            }

            return query;
        }
    }
}
